/*
 * Created on 21.07.2006 by leck
 *
 */
package de.devisnik.shifting.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.swt.graphics.Point;

import de.devisnik.shifting.IHole;
import de.devisnik.shifting.IPiece;
import de.devisnik.shifting.IPuzzle;
import de.devisnik.shifting.IPuzzleListener;
import de.devisnik.shifting.ShiftingEvent;

public class Puzzle implements IPuzzle {

    private int itsDimX;
    private int itsDimY;
    private Piece[][] itsPieces;
    private Hole itsHole;
    private Point itsHolePosition;
    private List/*<IPuzzleListener>*/ itsShiftingGameListeners = new ArrayList/*<IPuzzleListener>*/(); 

    public Puzzle(int x, int y) {
        itsDimX = x;
        itsDimY = y;
        createPieces(x, y);
    }

    public IHole getHole() {
        return itsHole;
    }
    
    private void createPieces(int dimX, int dimY) {
        itsPieces = new Piece[dimX][dimY];
        for (int y = 0; y < dimY; y++) {
            for (int x = 0; x < dimX; x++) {
                itsPieces[x][y] = createPieceAt(x, y);
            }
        }
        itsHole = new Hole(dimX - 1, dimY - 1, "<H>");
        itsHolePosition = new Point(dimX - 1, dimY - 1);
        itsPieces[dimX - 1][dimY - 1] = itsHole;
    }

    private void exchange(int firstX, int firstY, int secondX, int secondY) {
        Piece buffer = itsPieces[firstX][firstY];
        putPieceAt(itsPieces[secondX][secondY], firstX, firstY);
        putPieceAt(buffer, secondX, secondY);
    }

    private void putPieceAt(Piece piece, int x, int y) {
        itsPieces[x][y] = piece;
        piece.setPosition(x, y);
    }

    private Piece createPieceAt(int x, int y) {
        return new Piece(x, y, "" + (y * itsDimX + x));
    }

    public Point getSize() {
        return new Point(itsDimX, itsDimY);
    }

    private ShiftingEvent moveHoleDown() {
        if (itsHolePosition.y == itsDimY - 1) {
            return null;
        }
        IPiece piece = getPieceAt(itsHolePosition.x, itsHolePosition.y + 1);
        Point oldPosition = piece.getPosition();
        exchange(itsHolePosition.x, itsHolePosition.y, itsHolePosition.x, itsHolePosition.y + 1);
        Point newPosition = piece.getPosition();
        itsHolePosition.y++;
        return new ShiftingEvent(piece, oldPosition, newPosition);
    }

    private ShiftingEvent moveHoleLeft() {
        if (itsHolePosition.x == 0) {
            return null;
        }
        IPiece piece = getPieceAt(itsHolePosition.x-1, itsHolePosition.y);
        Point oldPosition = piece.getPosition();
        exchange(itsHolePosition.x, itsHolePosition.y, itsHolePosition.x - 1, itsHolePosition.y);
        Point newPosition = piece.getPosition();
        itsHolePosition.x--;
        return new ShiftingEvent(piece, oldPosition, newPosition);
    }

    private ShiftingEvent moveHoleRight() {
        if (itsHolePosition.x == itsDimX - 1) {
            return null;
        }
        IPiece piece = getPieceAt(itsHolePosition.x+1, itsHolePosition.y);
        Point oldPosition = piece.getPosition();
        exchange(itsHolePosition.x, itsHolePosition.y, itsHolePosition.x + 1, itsHolePosition.y);
        Point newPosition = piece.getPosition();
        itsHolePosition.x++;
        return new ShiftingEvent(piece, oldPosition, newPosition);
    }

    private ShiftingEvent moveHoleUp() {
        if (itsHolePosition.y == 0) {
            return null;
        }
        IPiece piece = getPieceAt(itsHolePosition.x, itsHolePosition.y -1);
        Point oldPosition = piece.getPosition();
        exchange(itsHolePosition.x, itsHolePosition.y, itsHolePosition.x, itsHolePosition.y - 1);
        Point newPosition = piece.getPosition();
        itsHolePosition.y--;
        return new ShiftingEvent(piece, oldPosition, newPosition);
    }

//    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        for (int y = 0; y < itsDimY; y++) {
            for (int x = 0; x < itsDimX; x++) {
                String label = itsPieces[x][y].getLabel();
                stringBuffer.append(label);
                stringBuffer.append(" ");
            }
            stringBuffer.append("\n");
        }
        return stringBuffer.toString();
    }

    public static void main(String[] args) {
        Puzzle shiftingGame = new Puzzle(5, 5);
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleUp();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleUp();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleLeft();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleLeft();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleUp();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleLeft();
        System.out.println(shiftingGame.toString());
        shiftingGame.moveHoleDown();
        System.out.println(shiftingGame.toString());
    }

    public IPiece getPieceAt(int posX, int posY) {
        return itsPieces[posX][posY];
    }

    public boolean shiftPieceAt(int posX, int posY) {
        if (itsHolePosition.x == posX && itsHolePosition.y == posY) {
            return false;
        }
        if (posX < 0 || posX >= itsDimX) {
            return false;
        }
        if (posY < 0 || posY >= itsDimY) {
            return false;
        }
        List/*<ShiftingEvent>*/ eventList = new ArrayList/*<ShiftingEvent>*/();
        if (itsHolePosition.x == posX) {
            if (itsHolePosition.y < posY) {
                int diffY = posY - itsHolePosition.y;
                for (int i = 0; i < diffY; i++) {
                    ShiftingEvent shiftingEvent = moveHoleDown();
                    eventList.add(shiftingEvent);
                }
            }
            else if (itsHolePosition.y > posY) {
                int diffY = itsHolePosition.y - posY;
                for (int i = 0; i < diffY; i++) {
                    ShiftingEvent shiftingEvent = moveHoleUp();
                    eventList.add(shiftingEvent);
                }
            }
        }
        else if (itsHolePosition.y == posY) {
            if (itsHolePosition.x < posX) {
                int diffX = posX - itsHolePosition.x;
                for (int i = 0; i < diffX; i++) {
                    ShiftingEvent shiftingEvent = moveHoleRight();
                    eventList.add(shiftingEvent);
                }
            }
            else if (itsHolePosition.x > posX) {
                int diffX = itsHolePosition.x - posX;
                for (int i = 0; i < diffX; i++) {
                    ShiftingEvent shiftingEvent = moveHoleLeft();
                    eventList.add(shiftingEvent);
                }
            }
        }
        fireShiftingEvents(eventList);
        return true;
    }

    private void fireShiftingEvents(List/*<ShiftingEvent>*/ eventList) {
        ShiftingEvent[] eventArray = (ShiftingEvent[]) eventList.toArray(new ShiftingEvent[eventList.size()]);
        for (Iterator/*<IPuzzleListener>*/ iter = itsShiftingGameListeners.iterator(); iter.hasNext();) {
            IPuzzleListener listener = (IPuzzleListener) iter.next();            
            listener.handleShifting(eventArray);
        }
    }

    public void addGameListener(IPuzzleListener listener) {
        itsShiftingGameListeners.add(listener);
    }

    public void removeGameListener(IPuzzleListener listener) {
        itsShiftingGameListeners.remove(listener);
    }
}
